import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import requests
import threading
import sqlite3
import queue
import json
import time
from datetime import datetime
from concurrent.futures import ThreadPoolExecutor, as_completed
# 正式版本 修改标题和到期时间
class PhoneStatusChecker:
    MAX_BATCH_SIZE = 50  # APISpace每批最多50个号码
    MAX_THREADS = 5      # 最大并发线程数
    
    def __init__(self, root):
        self.root = root
        self.root.title("电话号码状态精准检测系统-试用版-琪睿科技")
        self.root.geometry("1000x650")
        
        # 初始化数据库队列
        self.db_queue = queue.Queue()
        self.start_db_thread()
        
        # 创建界面
        self.create_widgets()
        self.load_config()

    def start_db_thread(self):
        """启动数据库工作线程"""
        threading.Thread(
            target=self.db_worker,
            daemon=True
        ).start()

    def db_worker(self):
        """处理所有数据库操作（线程安全）"""
        conn = sqlite3.connect('phone_status1.db', check_same_thread=False)
        cursor = conn.cursor()
        cursor.execute('''CREATE TABLE IF NOT EXISTS config (id INTEGER PRIMARY KEY, apikey TEXT)''')
        cursor.execute('''CREATE TABLE IF NOT EXISTS history (
            id INTEGER PRIMARY KEY,
            phone TEXT,
            status TEXT,
            carrier TEXT,
            area TEXT,
            details TEXT,
            timestamp DATETIME
        )''')
        conn.commit()
        
        while True:
            task, data = self.db_queue.get()
            if task == "save_config":
                cursor.execute("DELETE FROM config")
                cursor.execute("INSERT INTO config (apikey) VALUES (?)", (data,))
            elif task == "load_config":
                cursor.execute("SELECT apikey FROM config ORDER BY id DESC LIMIT 1")
                row = cursor.fetchone()
                if row:
                    self.root.after(0, lambda: self.api_key_var.set(row[0]))
            elif task == "save_history":
                phone, status, carrier, area, details = data
                timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                cursor.execute(
                    "INSERT INTO history (phone, status, carrier, area, details, timestamp) VALUES (?, ?, ?, ?, ?, ?)",
                    (phone, status, carrier, area, details, timestamp)
                )
            elif task == "export_history":
                cursor.execute("SELECT phone, status, carrier, area, details, timestamp FROM history")
                rows = cursor.fetchall()
                self.root.after(0, lambda: self.export_to_csv(data, rows))
            elif task == "clear_history":
                cursor.execute("DELETE FROM history")
            conn.commit()
            self.db_queue.task_done()

    def create_widgets(self):
        # API 配置区域
        api_frame = ttk.LabelFrame(self.root, text="APISpace 配置")
        api_frame.pack(fill="x", padx=10, pady=5)
        
        ttk.Label(api_frame, text="API 密钥:").grid(row=0, column=0, padx=5, pady=5)
        self.api_key_var = tk.StringVar()
        api_entry = ttk.Entry(api_frame, textvariable=self.api_key_var, width=60)
        api_entry.grid(row=0, column=1, padx=5, pady=5)
        ttk.Button(api_frame, text="保存", command=self.save_config).grid(row=0, column=2, padx=5)

        # 号码输入区域
        input_frame = ttk.LabelFrame(self.root, text="输入待检测号码（每行一个）")
        input_frame.pack(fill="x", padx=10, pady=5)
        
        self.phone_input = tk.Text(input_frame, height=10)
        self.phone_input.pack(fill="both", expand=True, padx=5, pady=5)
        
        btn_frame = ttk.Frame(input_frame)
        btn_frame.pack(fill="x", padx=5, pady=5)
        ttk.Button(btn_frame, text="导入文件", command=self.import_phones).pack(side="left")
        ttk.Button(btn_frame, text="清空", command=self.clear_input).pack(side="left")
        ttk.Button(btn_frame, text="开始检测", command=self.start_check).pack(side="right")

        # 结果展示区域
        result_frame = ttk.LabelFrame(self.root, text="检测结果")
        result_frame.pack(fill="both", expand=True, padx=10, pady=5)
        
        columns = ("phone", "status", "carrier", "area", "details")
        self.result_tree = ttk.Treeview(result_frame, columns=columns, show="headings")
        
        # 设置列宽
        col_widths = [120, 100, 120, 120, 300]
        for col, width in zip(columns, col_widths):
            self.result_tree.heading(col, text=col.capitalize())
            self.result_tree.column(col, width=width, anchor="w")
        
        scrollbar = ttk.Scrollbar(result_frame, orient="vertical", command=self.result_tree.yview)
        self.result_tree.configure(yscrollcommand=scrollbar.set)
        self.result_tree.pack(side="left", fill="both", expand=True)
        scrollbar.pack(side="right", fill="y")

        # 状态栏
        self.status_var = tk.StringVar(value="就绪")
        status_bar = ttk.Label(self.root, textvariable=self.status_var, relief="sunken", anchor="w")
        status_bar.pack(side="bottom", fill="x")

        # 进度条
        self.progress_var = tk.DoubleVar()
        self.progress_bar = ttk.Progressbar(
            self.root, variable=self.progress_var, maximum=100, mode="determinate"
        )
        self.progress_bar.pack(fill="x", padx=10, pady=5)

        # 操作按钮
        action_frame = ttk.Frame(self.root)
        action_frame.pack(fill="x", padx=10, pady=5)
        ttk.Button(action_frame, text="导出结果", command=self.export_results).pack(side="left")
        ttk.Button(action_frame, text="清除历史", command=self.clear_history).pack(side="left")

    def load_config(self):
        self.db_queue.put(("load_config", None))

    def save_config(self):
        api_key = self.api_key_var.get().strip()
        if not api_key:
            messagebox.showerror("错误", "API密钥不能为空")
            return
        self.db_queue.put(("save_config", api_key))
        messagebox.showinfo("成功", "配置已保存")

    def import_phones(self):
        file_path = filedialog.askopenfilename(filetypes=[("文本文件", "*.txt"), ("CSV", "*.csv")])
        if not file_path: 
            return
        try:
            with open(file_path, "r", encoding="utf-8") as f:
                phones = f.readlines()
                self.phone_input.delete("1.0", tk.END)
                self.phone_input.insert("1.0", "".join(phones))
            self.status_var.set(f"已导入 {len(phones)} 个号码")
        except Exception as e:
            messagebox.showerror("错误", f"文件读取失败: {str(e)}")

    def clear_input(self):
        self.phone_input.delete("1.0", tk.END)

    def start_check(self):
         # 新增有效期验证
        expiry_date = datetime(2025, 6, 29)
        current_date = datetime.now()
        
        if current_date > expiry_date:
            messagebox.showerror("授权过期", "软件已到期，请联系QQ 494514014获取新版本")
            return
        api_key = self.api_key_var.get().strip()
        if not api_key:
            messagebox.showerror("错误", "请先配置API密钥")
            return
            
        phones_text = self.phone_input.get("1.0", tk.END).strip()
        if not phones_text:
            messagebox.showerror("错误", "请输入要检测的号码")
            return
            
        phones = [p.strip() for p in phones_text.splitlines() if p.strip()]
        if not phones:
            return

        # 清空旧结果
        for item in self.result_tree.get_children():
            self.result_tree.delete(item)
            
        # 重置进度条
        self.progress_var.set(0)
        self.progress_bar["maximum"] = len(phones)
        
        # 启动检测线程
        self.status_var.set(f"开始检测 {len(phones)} 个号码...")
        threading.Thread(
            target=self.check_phones,
            args=(phones, api_key),
            daemon=True
        ).start()

    def check_phones(self, phones, api_key):
        """多线程分批检测号码状态"""
        # 1. 过滤并验证号码格式
        valid_phones = self.filter_valid_phones(phones)
        if not valid_phones:
            self.status_var.set("错误：未找到有效的中国大陆手机号")
            return
            
        # 2. 分批处理（每批最多50个）
        batches = []
        for i in range(0, len(valid_phones), self.MAX_BATCH_SIZE):
            batch = valid_phones[i:i+self.MAX_BATCH_SIZE]
            batches.append(batch)
            
        total_batches = len(batches)
        processed_count = 0
        
        # 3. 使用线程池执行批量检测
        with ThreadPoolExecutor(max_workers=self.MAX_THREADS) as executor:
            futures = {}
            for batch_idx, batch in enumerate(batches):
                future = executor.submit(
                    self.check_batch, 
                    batch, 
                    api_key, 
                    batch_idx,
                    total_batches
                )
                futures[future] = batch
                
            # 处理完成的结果
            for future in as_completed(futures):
                batch = futures[future]
                try:
                    results = future.result()
                    processed_count += len(batch)
                    self.progress_var.set(processed_count)
                    
                    # 更新UI
                    for result in results:
                        phone = result.get("phone", "")
                        status = result.get("status_text", "未知")
                        carrier = result.get("carrier", "未知")
                        area = result.get("area", "")
                        details = result.get("details", "")
                        
                        self.update_result_tree(phone, status, carrier, area, details)
                        self.db_queue.put(("save_history", (phone, status, carrier, area, details)))
                    
                    self.status_var.set(f"已处理: {processed_count}/{len(valid_phones)}")
                except Exception as e:
                    # 整批标记为错误
                    for phone in batch:
                        self.update_result_tree(phone, "错误", "", "", f"批量处理失败: {str(e)}")
        
        self.status_var.set(f"检测完成! 共处理 {len(valid_phones)} 个有效号码")

    def filter_valid_phones(self, phones):
        """过滤出有效的中国大陆手机号"""
        valid_phones = []
        for phone in phones:
            # 简单验证：11位数字，以1开头
            if len(phone) == 11 and phone.isdigit() and phone.startswith('1'):
                valid_phones.append(phone)
            else:
                self.update_result_tree(phone, "无效格式", "", "", "非中国大陆手机号格式")
        return valid_phones

    def check_batch(self, batch, api_key, batch_idx, total_batches):
        """检测一批号码（最多50个）"""
        url = "https://eolink.o.apispace.com/konghao/batch-ucheck"
        headers = {
            "X-APISpace-Token": api_key,
            "Authorization-Type": "apikey",
            "Content-Type": "application/x-www-form-urlencoded"
        }
        
        # 构建符合APISpace要求的请求体
        mobiles = ",".join(batch)
        payload = f"mobiles={mobiles}&type=0"
        
        # 添加批次延迟，避免请求过于频繁
        if batch_idx > 0 and batch_idx % self.MAX_THREADS == 0:
            time.sleep(1)  # 每完成MAX_THREADS个批次休息1秒
        
        try:
            response = requests.post(url, headers=headers, data=payload, timeout=30)
            
            if response.status_code != 200:
                raise Exception(f"API请求失败: {response.status_code} - {response.reason}")
                
            data = response.json()
            
            # 调试：打印API响应
            print(f"API响应: {json.dumps(data, indent=2, ensure_ascii=False)}")
            
            # 检查API返回状态码
            if data.get("code") != "200000":
                error_msg = data.get("message", "未知错误")
                raise Exception(f"API返回错误: {error_msg}")
                
            # 解析批量结果 - 根据实际API响应结构
            result_list = data.get("data", [])
            if not result_list:
                raise Exception("API返回空结果列表")
                
            # 处理每个号码的结果
            results = []
            for result in result_list:
                phone = result.get("mobile", "")
                status_code = result.get("status", "")
                carrier = result.get("numberType", "未知")
                area = result.get("area", "")
                last_time = result.get("lastTime", "")
                
                # 映射状态
                status_text, details = self.map_status(status_code, carrier, area, last_time)
                
                results.append({
                    "phone": phone,
                    "status_text": status_text,
                    "carrier": carrier,
                    "area": area,
                    "details": details
                })
            
            return results
            
        except Exception as e:
            # 返回错误信息用于批量处理
            error_results = []
            for phone in batch:
                error_results.append({
                    "phone": phone,
                    "status_text": "错误",
                    "carrier": "",
                    "area": "",
                    "details": f"检测失败: {str(e)}"
                })
            return error_results

    def map_status(self, status_code, carrier, area, last_time):
        """将API状态码映射为友好状态"""
        # 确保状态码是字符串
        if status_code is None:
            status_code = ""
        else:
            status_code = str(status_code)
        
        # 状态码映射表（根据API响应）
        status_map = {
            "0": ("❌ 空号", "空号"),
            "1": ("✅ 在用", "号码正常使用中"),
            "2": ("❌ 停机", "号码停机"),
            "3": ("☎️ 库无", "库无"),
            "4": ("❌ 空号", "号码为沉默号"),
            "5": ("🔋 关机", "风险号"),
            "7": ("⚠️ 疑似关机", "可能已关机或信号弱"),
            "13": ("⛔ 停机", "号码因欠费或主动申请停机"),
            "12": ("❌ 空号", "不存在的号码"),
            "9": ("⚠️ 服务器异常", "运营商系统繁忙"),
            "10": ("⚠️ 未知状态", "未能获取明确状态")
        }
        
        # 获取状态信息
        status_text, details = status_map.get(status_code, ("⚠️ 未识别", f"未知状态码: {status_code}"))
        
        # 添加附加信息
        if area:
            details += f" | 地区: {area}"
        
        if last_time:
            try:
                # 将时间戳转换为可读格式
                timestamp = int(last_time) / 1000  # 毫秒转秒
                last_active = datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M")
                details += f" | 最后活跃: {last_active}"
            except:
                pass
        
        # 特殊状态补充说明
        if status_code == "13":  # 停机
            details += "，建议联系用户充值"
        elif status_code in ["2", "4", "12"]:  # 空号
            details += "，请从数据库中移除"
        elif status_code == "5":  # 关机
            details += "，请稍后重试"
        
        return status_text, details

    def update_result_tree(self, phone, status, carrier, area, details):
        """线程安全更新结果列表"""
        self.root.after(0, lambda: self.result_tree.insert(
            "", "end", values=(phone, status, carrier, area, details)
        ))

    def export_results(self):
        file_path = filedialog.asksaveasfilename(
            defaultextension=".csv",
            filetypes=[("CSV文件", "*.csv")]
        )
        if file_path:
            self.db_queue.put(("export_history", file_path))

    def export_to_csv(self, file_path, rows):
        try:
            with open(file_path, "w", encoding="utf-8", newline='') as f:
                f.write("手机号,状态,运营商,地区,详细信息,检测时间\n")
                for row in rows:
                    # 转义特殊字符
                    escaped_row = [f'"{col}"' if any(char in str(col) for char in ',"\n') else str(col) for col in row]
                    f.write(",".join(escaped_row) + "\n")
            messagebox.showinfo("成功", f"已导出 {len(rows)} 条记录到:\n{file_path}")
        except Exception as e:
            messagebox.showerror("导出失败", f"导出失败: {str(e)}")

    def clear_history(self):
        if messagebox.askyesno("确认", "确定清除所有历史记录吗？"):
            for item in self.result_tree.get_children():
                self.result_tree.delete(item)
            # 清除数据库历史
            self.db_queue.put(("clear_history", None))
            self.status_var.set("历史记录已清除")

if __name__ == "__main__":
    root = tk.Tk()
    app = PhoneStatusChecker(root)
    root.mainloop()